/*
 * This file is Copyright 2002-2006 Loyola University Chicago,
 * Department of Computer Science and the Emerging Technologies Laboratory.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License via
 *    http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.citep.web.admin.accounts.client;

import java.util.Date;
import java.util.ArrayList;
import java.util.HashMap;

import com.citep.web.gwt.dto.*;
import com.citep.web.gwt.module.*;
import com.citep.web.gwt.cache.*;

import com.google.gwt.user.client.Window;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;



public class AccountEntryApplication extends BaseApplication {
	protected static int MAX_SHORTCUTS = 10;
	
	protected AccountEntryPresentation presentation;

	protected AccountServiceAsync accountsService;
	AsyncCallback saveCategoryCallBack;
	AsyncCallback saveCallBack;
	AsyncCallback editCallBack;
	AsyncCallback accountListCallBack;
	AsyncCallback editCompleteCallBack;
	AsyncCallback accountCategoryListCallBack;

	protected AccountDTO currentAccount;
	protected AccountTreeNode root;
	
	protected HashMap accountCategories; 	//Maps account category names to their respective id.
											//we need this because in the presentation layer, listboxes only
											//have the account category name as the value, but when we save
											//an account, we need to provide the id to the accountService.
	
	private AccountTreeCache cache = AccountTreeCache.getCache();
	
	private int accountsAdded = 0; //tracks how many accounts we have added to the shortcut list

	public AccountEntryApplication() {
		accountsService = (AccountServiceAsync)GWT.create(AccountService.class);
		((ServiceDefTarget)accountsService).setServiceEntryPoint(GWT.getModuleBaseURL() + "/accounts");
		
		accountCategories = new HashMap();
	}
	
	public void setPresentation(AccountEntryPresentation presentation) {
		this.presentation = presentation;
	}
	

	private AccountDTO findParent(AccountTreeNode start, AccountDTO account) {
		if(start == null)
			return null;
		
		AccountDTO current = start.getAccount();
		if(current.getId() == account.getId()) {
			if(start.getParent() != null)
				return start.getParent().getAccount();
			else
				return null;
		}
		
		if(start.getChildren() == null)
			return null;
		
		for(int i = 0; i < start.getChildren().size(); i++) {
			AccountDTO result = findParent((AccountTreeNode)start.getChildren().get(i),account);
			if(result != null)
				return result;
		}
		
		return null;
	}
	
	private AccountDTO findAccount(AccountTreeNode start, int id) {
		if(start == null)
			return null;
		
		AccountDTO account = start.getAccount();
		
		if(account.getId() == id)
			return account;
		
		if(start.getChildren() == null)
			return null;
		
		for(int i = 0; i < start.getChildren().size(); i++) {
			AccountDTO result = findAccount((AccountTreeNode)start.getChildren().get(i),id);
			if(result != null)
				return result;
		}
		
		return null;
			
	}

	public void createAccount(int parentAccountId, String name, String description, String type, String categoryName, String amount, Date startPeriod, Date endPeriod, boolean enabled) {
		presentation.setWaitState();
		
		Integer accountCategoryId = (Integer)accountCategories.get(categoryName);
		if(accountCategoryId == null)
			throw new RuntimeException("AccountEntryApplication:createAccount:null value for category id");
		
		AccountDTO parent = findAccount(root, parentAccountId);
		
		accountsService.createAccount(name, description, type, accountCategoryId.intValue(), amount, startPeriod, endPeriod, enabled, parent, getSaveCallBack());
	}

	protected AsyncCallback getSaveCategoryCallBack() {
		if(saveCategoryCallBack == null) {
			saveCategoryCallBack = new AsyncCallback() {
				public void onSuccess(Object result) {
					AccountCategoryDTO createdCategory = (AccountCategoryDTO)result;
					accountCategories.put(createdCategory.getName(), new Integer(createdCategory.getId()));
					presentation.onCategoryAdd(createdCategory.getName());
				}
				
				public void onFailure(Throwable caught) {
					processException(presentation,caught);
					presentation.setSaveReadyState();
				}
			};
		}
		
		return saveCategoryCallBack;
	}
	
	public void createAccountCategory(String name, String description) {
		accountsService.createAccountCategory(name, name, getSaveCategoryCallBack());
	}
	
	protected AsyncCallback getSaveCallBack() {
		
		if(saveCallBack == null) {
			saveCallBack = new AsyncCallback() {
				public void onSuccess(Object result) {
					AccountDTO createdAccount = (AccountDTO)result;
	
					presentation.displayStatus("Account saved", 3);
					presentation.addShortcut(new AccountShortcut(createdAccount.getName(),createdAccount.getId()), true);
					presentation.setSaveState();
				}
				
				public void onFailure(Throwable caught) {
					processException(presentation,caught);
					presentation.setSaveReadyState();
				}
			};
		}
			
		return saveCallBack;
	}
	
	
	public void getAccount(int accountId) {
		accountsService.getAccount(accountId, getEditCallBack());
	}

	protected AsyncCallback getEditCallBack() {
		if(editCallBack == null) {
			editCallBack = new AsyncCallback() {
				public void onSuccess(Object result) {
					AccountDTO retrievedAccount = (AccountDTO)result;
					presentation.setEditState();
					presentation.setAccountData(retrievedAccount);
					currentAccount = retrievedAccount;
					
					AccountDTO parent = findParent(root,retrievedAccount);
					if(parent != null)
						presentation.setSelectedParent(parent.getId());
				}
				
				public void onFailure(Throwable caught) {
					processException(presentation,caught);
					presentation.setEditReadyState();
				}
			};
		}
		
		return editCallBack;
	}
	
	
	public void saveCurrentAccount() {
		if(currentAccount != null) {
			presentation.getAccountData(currentAccount);
			int accountParentId = presentation.getSelectedParentId();
			AccountDTO parent = findAccount(root,accountParentId);
			accountsService.saveAccount(currentAccount, parent, getEditCompleteCallBack());			
		}
	}

	protected AsyncCallback getEditCompleteCallBack() {
		
		if(editCompleteCallBack == null) {
			editCompleteCallBack = new AsyncCallback() {
				public void onSuccess(Object result) {
					presentation.displayStatus("Account updated", 3);
					presentation.setEditState();
					presentation.updateShortcut(new AccountShortcut(currentAccount.getName(),currentAccount.getId()), true);
				}
				
				public void onFailure(Throwable caught) {
					processException(presentation,caught);
					presentation.setEditReadyState();
				}
			};
		}
			
		return editCompleteCallBack;
	}
	
	
	public void refreshAccountList() {

		//get the account tree from the cache
		boolean forceCall = root == null;
		cache.getAccountTree(new AccountTreeListenerAdapter() {
			public void onAccountTreeReady(AccountTreeNode node) {

				presentation.clearShortcuts();
				presentation.clearParentAccountList();

				root = node;
				populateShortcuts(root);
				
				if(root != null) {
					//create a fake account so that the user
					//can select "none" for a parent account
					AccountDTO fakeRoot = new AccountDTO();
					fakeRoot.setName("None");
					fakeRoot.setId(root.getAccount().getId());
					presentation.addAccountToParentAccountList(fakeRoot,0);
					
					if(root.getChildren() != null) {
						for(int i = 0; i < root.getChildren().size(); i++) {
							AccountTreeNode current = (AccountTreeNode)root.getChildren().get(i);
							addAccountToPresentation(current,0);
						}
					}
				}
			}
			
			public void onAccountTreeLoadFailure(Throwable t) {
				presentation.displayError("", t.getMessage());
			}
			
		}, forceCall);
	}
	
	private void addAccountToPresentation(AccountTreeNode node, int level) {
		if(node == null)
			return;
		
		presentation.addAccountToParentAccountList(node.getAccount(), level);
		
		if(node.getChildren() != null) {
			for(int i = 0; i < node.getChildren().size(); i++) {
				AccountTreeNode current = (AccountTreeNode)node.getChildren().get(i);
				addAccountToPresentation(current,level+1);
			}
		}
	}
	
	private void populateShortcuts(AccountTreeNode root) {
		accountsAdded = 0;
		for(int i = 0; i < root.getChildren().size(); i++) {
			if(accountsAdded++ > MAX_SHORTCUTS)
				break;

			AccountTreeNode current = (AccountTreeNode)root.getChildren().get(i);
			addAccountToShortcuts(current,0);
		}
	}
	
	private void addAccountToShortcuts(AccountTreeNode node, int level) {
		if(node == null)
			return;
		
		presentation.addShortcut(new AccountShortcut(node.getAccount().getName(),node.getAccount().getId()), false);		
		
		if(node.getChildren() != null) {			
			for(int i = 0; i < node.getChildren().size(); i++) {
				if(accountsAdded++ > MAX_SHORTCUTS)
					break;
				
				AccountTreeNode current = (AccountTreeNode)node.getChildren().get(i);
				addAccountToShortcuts(current,level+1);
			}
		}
	}

	public void refreshCategoryList() {
		accountCategories.clear();
		presentation.clearCategories();
		accountsService.getAccountCategories(getAccountCategoryListCallBack());
	}
	
	protected AsyncCallback getAccountCategoryListCallBack() {
		if(accountCategoryListCallBack == null) {
			accountCategoryListCallBack = new AsyncCallback() {
				public void onSuccess(Object result) {
					ArrayList categoryList = (ArrayList)result;
					
					for(int i = 0; i < categoryList.size(); i++) {
						AccountCategoryDTO category = (AccountCategoryDTO)categoryList.get(i);
						accountCategories.put(category.getName(), new Integer(category.getId()));
						presentation.onCategoryAdd(category.getName());
					}
				}
				
				public void onFailure(Throwable caught) {
					processException(presentation,caught);
				}
			};
		}
		
		return accountCategoryListCallBack;
	}
}
